const assert = require("assert")
const Router = require("express").Router
const router = new Router()
const moment = require('moment');
// RSA加密
const NodeRSA = require("node-rsa")
// AES加密
const cryptoJS = require("crypto-js")
const {errorWrapper} = require("../utils/errorWrapper")
const {success} = require("../utils/success")
//数据库表
const Key = require("../models/models/Key")
const User = require("../models/models/User")
const Admin = require("../models/models/Admini")
const Contest = require("../models/models/Contest")
const Notice = require("../models/models/Notice")
const Apply = require("../models/models/Apply")
const Team = require("../models/models/Team")
const Mid = require("../models/models/Mid")
const Work = require("../models/models/Work")
const RequestNum = require("../models/models/RequestNum")
const AESKEY = "hello"
const fs = require("fs")

// 引入表

const {Token, expiresTime} = require("../models/models/Token")
const mammoth = require("mammoth");
const mongoose = require("mongoose");
const Admini = require("../models/models/Admini");
const {raw} = require("express");

// 学生用户注册
router.post("/Register", errorWrapper(async (req, res) => {
    let {account, pwd, KeyID} = req.body
    let key = await Key.findById({_id: KeyID})
    // 用AES加密保存到数据库中，实现双重加密
    pwd = cryptoJS.AES.encrypt(decryptRAS(key, pwd), AESKEY).toString()
    // 注册成功保存到数据库中
    const user = await new User({Sno: account, pwd}).save()
    //关联用户ID和密钥文档
    key.userID = user._id
    key.updateTime = new Date()
    key.save()
    res.json(success())
}))

// 用户登录
router.post("/SignUp", errorWrapper(async (req, res) => {
    const {isAdmini} = req.body
    const re = await RequestNum.findOne()
    await RequestNum.findByIdAndUpdate(re._id, {num: re.num + 1})
    if (!isAdmini) {
        await UserLogin(req, res)
    } else {
        await AdminLogin(req, res)
    }
}))

// 登出
router.post("/signOut", errorWrapper(async (req, res) => {
    const {token, admin} = req.cookies
    console.log(admin)
    console.log(token)
    assert.ok(token, "token不存在")

    await Token.findByIdAndDelete({_id: token})
    res.cookie("token", '', {maxAge: 0})
    res.cookie("admin", '', {maxAge: 0})

    res.json(success())
}))

// 获取用户账号数据
router.post("/GetUserInfoAccount", errorWrapper(async (req, res) => {
    const {account} = req.body
    const r = await User.findOne({Sno: account}, "Sno")
    res.json(success({account: r}))
}))
// 个人信息认证
router.post("/UserInfoConfirm", errorWrapper(async (req, res) => {
    const {_id, Sno, name, tel, IDCard, schoolName, collegeName, majorName, contestID, sex} = req.body
    const user = await User.findByIdAndUpdate(_id, {
        name, tel, IDCard, schoolName, collegeName, majorName, contestID, sex, status: 1
    })
    res.json(success({user}))
}))

// 获取用户数据
router.post("/GetUserInfo", errorWrapper(async (req, res) => {
    const {token, admin} = req.cookies
    // 判断token是否过期
    const t = await Token.findById(token)

    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    if (moment(t.expiresTime).isBefore(moment())) {
        // token已过期
        await Token.deleteOne({_id: token})
        res.cookie("token", "", {maxAge: 0})
        res.json(success({msg: 'token过期'}))
    } else {
        if (admin !== "false") {
            const adminUser = await Admin.findById(t.userID, "-pwd -createTime -updateTime")
            res.json(success({adminUser: adminUser ? adminUser : null, msg: null, admin: "true"}))
        } else {
            const user = await User.findById(t.userID, "-pwd -createTime -updateTime")
            res.json(success({user: user ? user : null, msg: null, admin: "false"}))
        }
    }
}))

// 创建密钥用于用户注册
router.post("/GetPublicKey", errorWrapper(async (req, res) => {
    const key = new NodeRSA({b: 512})
    key.generateKeyPair(2048, 65537)
    // 导出密钥
    const publicKey = key.exportKey("pkcs1-public")
    const privateKey = key.exportKey("pkcs1-private")
    // 存入数据库
    const r = await new Key({publicKey, privateKey, userID: ''}).save()
    res.json(success({publicKey, id: r._id}))
}))

//通过账号获取公钥
router.post("/GetPublicByAccountKey", errorWrapper(async (req, res) => {
    const {account, isAdmini} = req.body

    if (!isAdmini) {
        const user = await User.findOne({Sno: account})
        const key = user ? await Key.findOne({userID: user._id.toString()}) : ''
        res.json(success({publicKey: key.publicKey}))
    } else {
        const admin = await Admin.findOne({account: account})
        const key = admin ? await Key.findOne({userID: admin._id.toString()}) : ''
        res.json(success({publicKey: key.publicKey}))
    }

}))

// 获取竞赛类型
router.post("/GetRaceType", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------请求竞赛类型-------]');
    const genre = await Contest.aggregate([{$group: {_id: "$genre"}}])
    res.json(success({genre}))
}))

// 获取竞赛类型 和 已经报名的竞赛数据
router.post("/GetApplyAndRaceType", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------请求组队信息-------]');
    const {token} = req.cookies
    let result = []
    // 判断token是否过期
    const t = await Token.findById(token)

    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    const apply = await Apply.aggregate([
        {
            $lookup: {
                from: "users",
                localField: t.userID,
                foreignField: "_id",
                as: "user"
            }
        },
        {
            $lookup: {
                from: "contests",
                localField: "contestID",
                foreignField: "_id",
                as: "contest"
            }
        },
        {
            $project: {
                'contest.name': 1,
                'contest.raceType': 1,
                'contestID': 1,
                '_id': 0
            }
        },
        {
            $match: {
                $and: [{'contest.raceType': 'T1'}]
            }
        }
    ])

    apply.forEach(v => {
        const obj = {
            value: v.contestID,
            label: v.contest[0].name
        }
        result.push(obj)
    })
    console.log(JSON.stringify(result))
    res.json(success({result}))
}))

// 组队申请
router.post("/ApplyTeamConfirm", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------发起组队-------]');
    const {token} = req.cookies
    const {name, num, value} = req.body
    const t = await Token.findById(token)
    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }

    const tm = await Team.find({$and: [{userID: t.userID, contestID: value}]})
    if (tm.length === 0) {
        const user = await User.findById(t.userID)
        const children = [{userID: user._id, userName: user.name}]
        await new Team({
            name, num, contestID: value, userID: t.userID, children
        }).save()
        res.json(success({msg: ''}))
    } else {
        res.json(success({msg: '组队失败'}))
    }
}))


// 分页获取竞赛
router.post("/GetContestLimit", errorWrapper(async (req, res) => {
    let {limitNum, skipNum, raceType, status, genre} = req.body
    let query = []
    let param = {}
    if (!limitNum) limitNum = 5
    if (!skipNum) skipNum = 0
    if (raceType) param.raceType = raceType
    if (status) param.status = status
    if (genre) param.genre = genre
    query.push(param)
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取' + limitNum + '条竞赛数据-------]');
    const c = await Contest.find({$and: query}).limit(limitNum).skip(skipNum).sort({num: -1})
    let data = [...c]
    if (c) {
        for (let dataKey in c) {
            let ma = await mammoth.convertToHtml({path: JSON.parse(c[dataKey].content).path})
            data[dataKey].content = ma.value
            data[dataKey].img = JSON.parse(c[dataKey].img).path
        }
    }
    const count = await Contest.count({$and: query})
    res.json(success({data, count}))
}))

//搜索竞赛
router.post("/searchContest", errorWrapper(async (req, res) => {
    let {limitNum, skipNum, formQuery} = req.body
    if (!limitNum) limitNum = 5
    if (!skipNum) skipNum = 0

    const c = await Contest.find({
        $or: [{
            name: {$regex: formQuery}
        }, {
            genre: {$regex: formQuery}
        }]
    }).limit(limitNum).skip(skipNum).sort({createTime: -1})
    let data = [...c]
    if (c) {
        for (let dataKey in c) {
            let ma = await mammoth.convertToHtml({path: JSON.parse(c[dataKey].content).path})
            data[dataKey].content = ma.value
            data[dataKey].img = JSON.parse(c[dataKey].img).path
        }
    }
    const count = await Contest.count({
        $or: [{
            name: {$regex: formQuery}
        }, {
            genre: {$regex: formQuery}
        }]
    })
    res.json(success({data, count}))
}))

// 获取公告信息
router.post("/GetNotice", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取公告信息-------]');
    const {limit, skip} = req.body
    const c = await Notice.find().limit(limit).skip(skip)
    let data = [...c]
    for (const dataKey in c) {
        const param = JSON.parse(c[dataKey].content)
        const ma = await mammoth.convertToHtml({path: param.path})
        data[dataKey].content = ma.value
    }
    res.json(success({data}))
}))

// 报名申请
router.post("/ApplyFormSubmit", errorWrapper(async (req, res) => {
    const {userID, contestID} = req.body
    const apply = await Apply.find({
        $and: [{
            userID
        }, {contestID}]
    })

    if (apply.length === 0) {
        const apply = await new Apply({userID, contestID}).save()
        const user = await User.findByIdAndUpdate(userID, {$addToSet: {applyID: apply._id}})
        fs.appendFile('./log/log.txt', moment().format('YYYY-MM-DD hh:mm:ss') + '学生用户' + user.Sno + '登录成功\n', (err) => {
            if (err) {
                console.log('出错')
            }
        })
        res.json(success({msg: ''}))
    } else {
        res.json(success({msg: '已存在'}))
    }
}))

// 报名信息
router.post("/GetMyApplyInfoData", errorWrapper(async (req, res) => {
    const {token} = req.cookies
    // 判断token是否过期
    const t = await Token.findById(token)

    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    const user = await User.findById(t.userID, '-pwd -__v')
    const apply = await Apply.aggregate([
        {
            $lookup: {
                from: "contests",
                localField: "contestID",
                foreignField: "_id",
                as: "contest"
            }
        },
        {
            $match: {
                "_id": {$in: user.applyID}
            }
        }
    ])

    res.json(success({apply}))
}))

// 通过ID获取竞赛
router.post("/GetContestByID", errorWrapper(async (req, res) => {
    const {id} = req.body
    const c = await Contest.findById(id)
    const ma = await mammoth.convertToHtml({path: JSON.parse(c.content).path})
    c.content = ma.value
    c.img = JSON.parse(c.img).path
    res.json(success({c}))
}))

// 解散队伍
router.post("/deleteMyTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------解散队伍-------]');
    const {id} = req.body
    console.log(id)
    if (id) {
        await Team.findByIdAndDelete(id)
    } else {
        await Team.deleteMany()
    }
    res.json(success({}))
}))

// 加入队伍消息通知
router.post("/GetAllMessage", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取用户申请加入队伍消息通知-------]');
    const {token} = req.cookies
    const t = await Token.findById(token)
    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    const mid = await Mid.aggregate([
        {
            $lookup: {
                from: "teams",
                localField: "teamID",
                foreignField: "_id",
                as: "team"
            }
        }, {
            $lookup: {
                from: "users",
                localField: "applyID",
                foreignField: "_id",
                as: "user"
            }
        }, {
            $project: {
                "user.pwd": 0,
                "user.__v": 0,
            }
        }
    ])
    res.json(success({mid}))
}))

// 退出组队
router.post("/popMyTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------解散队伍-------]');
    const {id, children} = req.body
    await Team.findByIdAndUpdate(id, {children})
    res.json(success({}))
}))

// 同意加入 组队
router.post("/ConfirmJoinTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------解散队伍-------]');
    const {teamID, children, midID} = req.body
    await Team.findOneAndUpdate(teamID, {children})
    await Mid.findOneAndDelete(midID)
    res.json(success({}))
}))


// 获取我加入的队伍
router.post("/GetMyTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取用户自己的队伍信息-------]');
    const {token} = req.cookies
    const t = await Token.findById(token)
    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    const tm = await Team.aggregate([
        {
            $lookup: {
                from: "contests",
                localField: "contestID",
                foreignField: "_id",
                as: "contest"
            }
        }, {
            $project: {
                'contest.name': 1,
                'contestID': 1,
                'children': 1,
                'name': 1,
                '_id': 1,
                'createTime': 1,
                "updateTime": 1,
                'num': 1,
                'status': 1,
                'userID': 1,
            }
        }
    ])
    res.json(success({tm}))

}))

// 获取各学院的队伍

router.post("/GetTeamCollege", errorWrapper(async (req, res) => {
    const tm = await Team.aggregate([
        {
            $lookup: {
                from: "users",
                localField: "userID",
                foreignField: "_id",
                as: "user"
            }
        },
        {
            $group: {_id: '$user.collegeName'}
        }
    ])
    res.json(success({tm}))
}))


// 学生获取全部队伍
router.post("/GetUserAllTeam", errorWrapper(async (req, res) => {
    const {limit, skip, status, collegeName} = req.body
    let query = []
    let obj = {}
    if (status !== 'null') {
        obj.status = status
    }
    if (collegeName) {
        obj['user.collegeName'] = collegeName[0]
    }
    query.push(obj)
    if (limit && skip){
        const tm = await Team.aggregate([
            {
                $lookup: {
                    from: "users",
                    localField: "userID",
                    foreignField: "_id",
                    as: "user"
                }
            },
            {
                $lookup: {
                    from: "contests",
                    localField: "contestID",
                    foreignField: "_id",
                    as: "contest"
                }
            },
            {
                $project: {
                    'contest.name': 1,
                    'contestID': 1,
                    'children': 1,
                    'name': 1,
                    '_id': 1,
                    'createTime': 1,
                    "updateTime": 1,
                    'num': 1,
                    'status': 1,
                    'user.name': 1,
                    'user.collegeName': 1
                }
            },
            {
                $match: {$and: query}
            }
        ]).limit(limit).skip(skip)
        res.json(success({tm}))
    }else {
        const tm = await Team.aggregate([
            {
                $lookup: {
                    from: "users",
                    localField: "userID",
                    foreignField: "_id",
                    as: "user"
                }
            },
            {
                $lookup: {
                    from: "contests",
                    localField: "contestID",
                    foreignField: "_id",
                    as: "contest"
                }
            },
            {
                $project: {
                    'contest.name': 1,
                    'contestID': 1,
                    'children': 1,
                    'name': 1,
                    '_id': 1,
                    'createTime': 1,
                    "updateTime": 1,
                    'num': 1,
                    'status': 1,
                    'user.name': 1,
                    'user.collegeName': 1
                }
            }
        ])
        res.json(success({tm}))
    }

}))

// 管理员获取全部队伍
router.post("/GetAllTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取用户全部队伍信息-------]');
    const {name, collegeName} = req.body
    const {token} = req.cookies
    // 判断token是否过期
    const t = await Token.findById(token)
    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    let query = []
    let obj = {}
    if (name) {
        obj.name = name
    }
    const ad = await Admini.findById(t.userID)
    if (ad) {
        if (ad.role.some(v => v.includes('SuperAdmin')))
            obj['user.collegeName'] = collegeName
        else {
            obj['user.collegeName'] = ad.collegeName
        }
    }
    query.push(obj)
    const tm = await Team.aggregate([
        {
            $lookup: {
                from: "users",
                localField: "userID",
                foreignField: "_id",
                as: "user"
            }
        },
        {
            $lookup: {
                from: "contests",
                localField: "contestID",
                foreignField: "_id",
                as: "contest"
            }
        },
        {
            $project: {
                'contest.name': 1,
                'contestID': 1,
                'children': 1,
                'name': 1,
                '_id': 1,
                'createTime': 1,
                "updateTime": 1,
                'num': 1,
                'status': 1,
                'user.name': 1,
                'user.collegeName': 1
            }
        },
        {
            $match: {
                $and: query
            }
        }
    ])
    res.json(success({tm}))
}))

// 申请加入队伍
router.post("/joinTeam", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[-------获取用户申请加入队伍-------]');
    const {teamID, userID, applyID} = req.body
    await new Mid({teamID, userID, applyID}).save()
    res.json(success({}))
}))

// 上传作品
router.post("/uploadWork", errorWrapper(async (req, res) => {
    console.log(moment().format('YYYY 年 MM 月 DD 日 h:mm:ss a') + '学生用户[------上传作品-------]');
    const {isTeam, name, path, userID, teamID, contestID} = req.body
    const w = await Work.find({teamID})
    if (w.length === 0) {
        await Work.insertMany({isTeam, name, path, userID, teamID, contestID})
        res.json(success({upload: true}))
    } else {
        res.json(success({upload: false}))
    }
}))

// 获取作品信息
router.post("/GetMyWork", errorWrapper(async (req, res) => {
    const {token} = req.cookies
    // 判断token是否过期
    const t = await Token.findById(token)
    if (!t) {
        res.json(success({msg: 'token过期'}))
        return;
    }
    const w = await Work.aggregate([
        {
            $lookup: {
                from: "contests",
                localField: "contestID",
                foreignField: "_id",
                as: "contest"
            }
        },
        {
            $lookup: {
                from: "scores",
                localField: "scoreID",
                foreignField: "_id",
                as: "score"
            }
        }
    ])
    console.log(w)
    res.json(success({w}))
}))

// 解密
function decryptRAS(key, buffer) {
    const rsa = new NodeRSA({b: 512})
    rsa.importKey(key.publicKey, "pkcs1-public")
    rsa.importKey(key.privateKey, "pkcs1-private")
    const r = rsa.decrypt(Buffer.from(buffer.data))
    return r.toString()
}

// 学生用户登录
async function UserLogin(req, res) {
    let {account, pwd} = req.body
    // 断言
    assert.ok(account && typeof account === "string" && account.trim() !== '', "输入账号无效")
    assert.ok(pwd.toString() && typeof pwd.toString() === 'string' && pwd.toString().trim() !== '', "请输入有效密码")
    const user = await User.findOne({Sno: account})
    const key = user ? await Key.findOne({userID: user._id.toString()}) : null
    const DecryptPwd = user ? cryptoJS.AES.decrypt(user.pwd, AESKEY).toString(cryptoJS.enc.Utf8) : null
    pwd = key ? decryptRAS(key, pwd) : null
    if (!user) {
        console.log(`学生用户登录日志[---- 账号不存在  -----]`)
        res.json(success({msg: '账号不存在'}))
    } else if (pwd !== DecryptPwd) {
        console.log(`学生用户登录日志[---- 密码错误 -----]`)
        res.json(success({msg: '密码错误'}))
    } else {
        // 保存登录状态
        const token = await Token.findOneAndUpdate({userID: user._id.toString()}, {
            expiresTime: Date.now() + expiresTime,
            updateTime: new Date()
        }, {
            upsert: true, new: true
        })
        //设置登录状态和过期时间
        res.cookie("token", token._id, {httpOnly: true, maxAge: expiresTime})
        res.cookie("admin", false, {httpOnly: true, maxAge: expiresTime})
        fs.appendFile('./log/log.txt', moment().format('YYYY-MM-DD hh:mm:ss') + '学生用户' + user.Sno + '登录成功\n', (err) => {
            if (err) {
                console.log('出错')
            }
        })
        res.json(success({userID: user._id, userName: user.name, msg: ''}))
    }
}

// 管理员用户登录
async function AdminLogin(req, res) {
    let {account, pwd, isAdmini} = req.body
    // 断言
    assert.ok(account && typeof account === "string" && account.trim() !== '', "输入账号无效")
    assert.ok(pwd.toString() && typeof pwd.toString() === 'string' && pwd.toString().trim() !== '', "请输入有效密码")
    const admin = await Admin.findOne({account: account})

    const key = admin ? await Key.findOne({userID: admin._id.toString()}) : null
    const DecryptPwd = admin ? cryptoJS.AES.decrypt(admin.pwd, AESKEY).toString(cryptoJS.enc.Utf8) : null
    pwd = key ? decryptRAS(key, pwd) : null
    if (!admin) {
        console.log(`管理员用户登录日志[---- 账号不存在 -----]`)
        res.json(success({msg: '账号不存在 !'}))
    } else if (pwd !== DecryptPwd) {
        console.log(`管理员用户登录日志[---- 密码错误 -----]`)
        res.json(success({msg: '密码错误'}))
    } else {
        // 保存登录状态
        const token = await Token.findOneAndUpdate({userID: admin._id.toString()}, {
            expiresTime: Date.now() + expiresTime,
            updateTime: new Date()
        }, {
            upsert: true, new: true
        })
        //设置登录状态和过期时间
        res.cookie("token", token._id, {httpOnly: true, maxAge: expiresTime})
        res.cookie("admin", true, {httpOnly: true, maxAge: expiresTime})
        fs.appendFile('./log/log.txt', moment().format('YYYY-MM-DD hh:mm:ss') + '管理员用户' + admin.account + '登录成功\n', (err) => {
            if (err) {
                console.log('出错')
            }
        })
        res.json(success({msg: ''}))
    }
}


module.exports = router